#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define NUM 1024
#define OP_NUM 64

#define NONE_REDIR   0
#define INPUT_REDIR  1
#define OUTPUT_REDIR 2
#define APPEND_REDIR  3

#define trimSpace(start) do{\
            while(isspace(*start)) ++start;\
        }while(0)

char command[NUM];
char *myargv[OP_NUM];
int lastCode = 0;
int lastSig = 0;

int redirType = NONE_REDIR;
char *redirFile = NULL;

void commandCheck(char *commands)
{
    assert(commands);
    char *start = commands;
    char *end = commands + strlen(commands);

    while (start < end)
    {
        if (*start == '>')
        {
            *start = '\0';
            start++;
            if(*start == '>')
            {
                // "ls -a >> test.txt"
                redirType = APPEND_REDIR;
                start++;
            }
            else
            {
                // "ls -a >    test.txt"
                redirType = OUTPUT_REDIR;
            }
            trimSpace(start);
            redirFile = start;
            break;
        }
        else if (*start == '<')
        {
            *start = '\0';
            start++;
            trimSpace(start);
            // 填写重定向信息
            redirType = INPUT_REDIR;
            redirFile = start;
            break;
        }
        else 
        {
            start++;
        }
    }
}

int main()
{
    while (1)
    {
        char *user = getenv("USER");
        char *host = getenv("HOSTNAME");
        printf("%s@%s 当前路径#", user, host);
        fflush(stdout);

        char *s = fgets(command, sizeof(command) -1, stdin);//获取命令行的输入
        assert(s != NULL);
        (void)s;
        //清除用户按下的回车
        command[strlen(command) - 1] = 0;

        commandCheck(command);
        //字符串切割
        myargv[0] = strtok(command, " ");
        int i = 1;

        //显示颜色
        if (myargv[0] != NULL && strcmp(myargv[0], "ls") == 0) 
        {
            myargv[i++] = (char*)"--color=auto";
        }

        //myargv[] 必须以NULL结尾，而strtok函数结束的时候返回NULL
        while (myargv[i++] = strtok(NULL, " ")) {}//NULL表示从上次结束的位置继续查找下一个子串

        if (myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if (myargv[1] != NULL) chdir(myargv[1]);
            continue;
        }

        if (myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if (strcmp(myargv[1], "$?") == 0)
            {
                printf("%d, %d\n", lastCode, lastSig);
            }
            else 
            {
                printf("%s\n", myargv[1]);
            }
            continue;
        }


#ifdef DEBUG 
        for (int i = 0; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif 
        //执行指令
        pid_t id = fork();
        assert(id != -1);

        if (id == 0)
        {
            switch(redirType)
            {
                case NONE_REDIR:
                    // 什么都不做
                    break;
                case INPUT_REDIR:
                    {
                        int fd = open(redirFile, O_RDONLY);
                        if(fd < 0){
                            perror("open");
                            exit(errno);
                        }
                        // 重定向的文件已经成功打开了
                        dup2(fd, 0);
                    }
                    break;
                case OUTPUT_REDIR:
                case APPEND_REDIR:
                    {
                        umask(0);
                        int flags = O_WRONLY | O_CREAT;
                        if(redirType == APPEND_REDIR) flags |= O_APPEND;
                        else flags |= O_TRUNC;
                        int fd = open(redirFile, flags, 0666);
                        if(fd < 0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        dup2(fd, 1);
                    }
                    break;
                default:
                    printf("bug?\n");
                    break;
            }

            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert(ret > 0);
        (void)ret;

        lastCode = WEXITSTATUS(status);
        lastSig = WIFEXITED(status);

    }
}
